// Copyright 2008 Senary
// See credits.txt for details.

// This file is part of Omega VII.

// Omega VII is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Omega VII is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Omega VII.  If not, see <http://www.gnu.org/licenses/>.

#include <string>

#include "Planet.h"
#include "Player.h"
#include "AIPlayer.h"
#include "GameManager.h"
#include "InputManager.h"

using namespace std;


Planet::Planet(PlanetType type) : _type(type)
{
	_shield = maxShield();
	_type = type;

	float rDist = (float)resourceDistribution();
	FreeLand = (float)maxPopulation();
	ResourceLand = (rDist/100) * FreeLand;
	FactoriesLand = 0;
	MinesLand = 0;
	ConstructionPoint = 2000;
	
}

Planet::~Planet(void)
{
}


// Inherited from CombatUnit
// Receive a certain amount of damage
// param damager: the unit that inflicts the damage.
void Planet::receiveDamage(double damage, CombatUnit* damager, float totalTime)
{
	if (_shield == maxShield()) {
		// a new combat has started...
		GameManager::getInstance()->appendMessage("Planet "
			+ Planet::stringFromPlanetType(_type) + " is under attack!");
	}

	CombatUnit::receiveDamage(damage, damager, totalTime);

	if (_shield <= 0) {
		_shield = maxShield();
		setOwner(damager->getOwner());

		GameManager::getInstance()->appendMessage("Planet "
			+ Planet::stringFromPlanetType(_type) + " has been pwned by "
			+ _owner->getName());

		GameManager* gm = GameManager::getInstance();
		if (gm->getLevel().isGameOver()) {
			for (std::vector<Player*>::const_iterator j = gm->getPlayers().begin();
				j != gm->getPlayers().end(); ++j) {
					if (!((*j)->getIsHumanPlayer()))
						dynamic_cast<AIPlayer*>(*j)->stop();
			}
			if (gm->getLevel().isVictory()) {
				gm->appendMessage("Glory belongs to you!!!");
			}
			else {
				gm->appendMessage("The race for Omega7 is lost in your hand.");
			}
		}
	}
}


//constant values (depend only on type)
// inherited from CombatUnit
int Planet::maxFirepower() const
{
	return 0;
}

//constant values (depend only on type)
// inherited from CombatUnit
int Planet::maxShield() const
{
	switch(_type){
		case PT_OMEGA_VII:
			return -1;
			break;
		case PT_EPLAX:
			return 7940; 
			break;
		case PT_KYAUYU:
			return 6710; 
			break;
		case PT_OPA_QUARTUS:
			return 2540; 
			break;
		case PT_OYCH:
			return 4369; 
			break;
		case PT_RYOULAG:
			return 5260; 
			break;
		case PT_TUN_DOUAARI:
			return 6240; 
			break;
		default:
			throw string("Planet::maxShield: Illegal planet type!");
			break;
	}
}

//constant values (depend only on type)
int Planet::maxPopulation(void) const
{
	switch(_type){
		case PT_OMEGA_VII:
			return -1;
			break;
		case PT_EPLAX:
			return 7943; 
			break;
		case PT_KYAUYU:
			return 6718; 
			break;
		case PT_OPA_QUARTUS:
			return 2546; 
			break;
		case PT_OYCH:
			return 4369; 
			break;
		case PT_RYOULAG:
			return 5261; 
			break;
		case PT_TUN_DOUAARI:
			return 6243; 
			break;
		default:
			throw string("Planet::maxPopulation: Illegal planet type!");
			break;
	}
}

//constant values (depend only on type)
double Planet::orbitHeight(void) const
{
	return maxPopulation() * RADIUS_PER_POPULATION + ORBIT_HEIGHT;
}

//constant values (depend only on type)
int Planet::resourceDistribution(void) const
{
	switch(_type){
		case PT_OMEGA_VII:
			return -1;
			break;
		case PT_EPLAX:
			return 21; 
			break;
		case PT_KYAUYU:
			return 32; 
			break;
		case PT_OPA_QUARTUS:
			return 70; 
			break;
		case PT_OYCH:
			return 56; 
			break;
		case PT_RYOULAG:
			return 45; 
			break;
		case PT_TUN_DOUAARI:
			return 27; 
			break;
		default:
			throw string("Planet::resourceDistribution: Illegal planet type!");
			break;
	}
}

//constant values (depend only on type)
int Planet::numDaysPerYear(void) const
{
	switch(_type){
		case PT_OMEGA_VII:
			return -1;
			break;
		case PT_EPLAX:
			return 350; 
			break;
		case PT_KYAUYU:
			return 312; 
			break;
		case PT_OPA_QUARTUS:
			return 122; 
			break;
		case PT_OYCH:
			return 245; 
			break;
		case PT_RYOULAG:
			return 149; 
			break;
		case PT_TUN_DOUAARI:
			return 762; 
			break;
		default:
			throw string("Planet::numDaysPerYear: Illegal planet type!");
			break;
	}
}

int Planet::buildFactory(int size)
{
	GameManager* gm = GameManager::getInstance();
	int cost = 0;
	//int income = gm->getPlayers()[0]->getIncome();
	int income = 0;
	//Calculate cost of factories
	cost = size * COSTFACTORY;
		
	if(getOwner() != NULL){

		income = getOwner()->getIncome();
		if(cost > income){
				gm->appendMessage("You need more gold to build that factory.");
			return -1;
		}
		
		if(income - cost < getOwner()->getUpkeep()){
			gm->appendMessage("Upkeep must be maintained, building of factories unsuccessful");
			return -1;
		}
		
		if(size > FreeLand){
			gm->appendMessage("Cannot add any more factories; there's no more free land.");
			return -2;
		}

		if(cost <= income && size <= FreeLand){
			//if enough gold to build factory
			//and requested land less than free land
			FactoriesLand += size;	//increase factories
			FreeLand -= size;		//decrease free land
			income -= cost;			//decrease income
		}
		
		
		
		this->getOwner()->setIncome(income);
	}


	return 1;

}

int Planet::buildMine(int size)
{
	GameManager* gm = GameManager::getInstance();
	//int income = gm->getPlayers()[0]->getIncome();
	int income = 0;
	int cost = 0;
	int rLandLeft =0;
	
	//Calculate cost of factories
	cost = size * COSTMINE;
	//calculate amount of land with resource left
	rLandLeft = (int)(ResourceLand - MinesLand);
		
	if(getOwner() != NULL){

		income = this->getOwner()->getIncome();

		if(cost > income){
			
			if(typeid(this->getOwner()) != typeid(AIPlayer)){
				gm->appendMessage("You need more gold to build that mine.");
				
			}
			return -1;
			
		}
		
		if(income - cost < getOwner()->getUpkeep()){
			gm->appendMessage("Upkeep must be maintained, building of mines unsuccessful");
			return -1;
		}

		if(size > FreeLand){
			
			if(typeid(this->getOwner()) != typeid(AIPlayer)){
				gm->appendMessage("Cannot add any more mines; there's no more free land.");
				
			}
			return -2;
		}
		if(size > rLandLeft){
			if(typeid(this->getOwner()) != typeid(AIPlayer)){
				gm->appendMessage("Cannot add any more mines; This planet does not have any resources left.");
				
			}
			return -3;
		}

		if(cost <= income && size <= FreeLand && size <= rLandLeft){
			//if enough gold to build factory
			//and requested land less than free land
				MinesLand += size;	//increase mines
				FreeLand -= size;	//decrease free land
				income -= cost;		//decrease income
		}
		
		
	
		this->getOwner()->setIncome(income);
	}
	

	return 1;
}


//int Planet::setLandRatio(float factorypercent, float minespercent){
//	float fland = (factorypercent/100)*FreeLand;
//	float mland = (minespercent/100)*FreeLand;
//
//	float landrequired = fland + mland;
//
//	if(landrequired>FreeLand){
//		if(typeid(this->getOwner()) != typeid(AIPlayer)){
//			str_cat(errstr, "\nCannot add any more mines and factories because exceeding free land.");
//		}
//		throw "\nCannot add any more mines and factories because exceeding free land.";
//		return -1;
//	}
//	else{
//		buildFactory((int)fland);
//		buildMine((int)mland);
//	}
//	return 1;
//
//}


//int Planet::destroyFactory(int size)
//{
//	if(size <= FactoriesLand){ //requested size less than factory land
//		FactoriesLand -= size;
//		
//	}
//	else{
//		if(typeid(this->getOwner()) != typeid(AIPlayer)){
//			str_cat(errstr, "Requested factories cannot be destroyed");
//		}
//		//throw"Requested factories cannot be destroyed";
//		return -1;
//	}
//
//	return 1;
//
//}
//
//int Planet::destroyMine(int size)
//{
//	if(size <= MinesLand){ //requested size less than mine land
//		MinesLand -= size;
//	}
//	else{
//		if(typeid(getOwner()) != typeid(AIPlayer)){
//			str_cat(errstr,"Requested factories cannot be destroyed");
//		}
//		throw "Requested factories cannot be destroyed";
//		return -1;
//	}
//
//	return 1;
//}

int Planet::orderSpaceship(int quantity, SpaceshipType type)
{

	GameManager* gm = GameManager::getInstance();
	float cost =0;
	Spaceship* ss = new Spaceship(type);
	Vector* vpos = new Vector(0,300,500);
	VECTOR* shipPos = new VECTOR(); //position of spaceship

	int retVal = -1;
	
	//calculate cost of spaceship	
	cost = (float)(quantity * ss->cost());

	
	if(cost<=ConstructionPoint){
		ConstructionPoint -= (int)cost;			//decrease income
		//increase spaceships
		for(int i=0;i<quantity;i++){
			
			ss = gm->createSpaceship(type);
			if(getOwner()!=0){
				ss->setOwner(getOwner());
				ss->setPosition(_position + Vector::randomVector() * orbitHeight());
				ss->startOrbit(this, m_totaltime);
			}			
		}
	}
	else{
		
		if(getOwner()->getIsHumanPlayer()){
			gm->appendMessage("Not enough construction points to build this "
				+ Spaceship::stringFromSpaceshipType(type) + ".");
		}
		retVal = -1;
	}
	
	
	

	return retVal;
	
}

void Planet::update(float timestep, float totalTime){
	
	GameManager* gm = GameManager::getInstance();
	float numberofweeks;
	float earnings;
	float gold=0;

	//generate construction points
	numberofweeks = (float)(timestep / WEEK_IN_SECONDS);
	//The amount of construction point generated is 10 CP/M/week
	ConstructionPoint += FactoriesLand*10*numberofweeks;
	
	// ugly totaltime hack...
	m_totaltime = totalTime;

	//generate gold
	//The amount of gold generated is 30gold/M/week
	earnings = MinesLand*30*numberofweeks;
	if(this->getOwner() != NULL){
		gold =	(float)(this->getOwner()->getIncome());
	}
	gold = gold + earnings;
	//gm->getPlayers()[0]->setIncome(gold);
	if(this->getOwner() != NULL){
		this->getOwner()->setIncome((int)gold);
	}
	
	// Update the underlying CombatUnit (healing included)
	CombatUnit::update(timestep, totalTime);
}

// Checks if the planet collides with another object
bool Planet::isInRange(SpaceObject const& other) const
{
	try {
		Spaceship const& ss = dynamic_cast<Spaceship const&>(other);
		return ss.isInRange(*this);

	} catch (bad_cast) {
		// planets don't collide with anything other than spaceships.
		return false;
	}
}


// Inherited from SpaceObject
void Planet::setOwner(Player * owner)
{
	if (_owner != 0) {
		_owner->removePlanet(this);
	}

	CombatUnit::setOwner(owner);

	if (_owner != 0) {
		_owner->addPlanet(this);
	}
}



float Planet::getFactoriesLand(){
	return FactoriesLand;
}

float Planet::getMinesLand(){
	return MinesLand;
}

float Planet::getFreeLand(){
	return FreeLand;
}

float Planet::getResourceLand(){
	return ResourceLand;
}

float Planet::getConstructionPoint(){
	return ConstructionPoint;
}

void Planet::setFactoriesLand(float fLand){
	FactoriesLand = fLand;
}

void Planet::setMinesLand(float mLand){
	MinesLand = mLand;
}

void Planet::setFreeLand(float frLand){
	FreeLand = frLand;
}

void Planet::setResourceLand(float rLand){
	ResourceLand = rLand;
}

void Planet::setConstructionPoint(float cp){
	ConstructionPoint = cp;
}

// Convert a string to planetType
PlanetType Planet::planetTypeFromString(std::string s)
{
	if (s == "OmegaVii") {
		return PT_OMEGA_VII;
	} else if (s == "Eplax") {
		return PT_EPLAX;
	} else if (s == "Kyauyu") {
		return PT_KYAUYU;
	} else if (s == "OpaQuartus") {
		return PT_OPA_QUARTUS;
	} else if (s == "Oych") {
		return PT_OYCH;
	} else if (s == "Ryoulag") {
		return PT_RYOULAG;
	} else if (s == "Tundouaari") {
		return PT_TUN_DOUAARI;
	}

	throw string("Planet::planetTypeFromString: illegal string");
}

// Convert a planetType to string
std::string Planet::stringFromPlanetType(PlanetType pt)
{
	switch(pt) {
		case PT_OMEGA_VII:
			return "OmegaVii";
			break;
		case PT_EPLAX:
			return "Eplax";
			break;
		case PT_OPA_QUARTUS:
			return "OpaQuartus";
			break;
		case PT_KYAUYU:
			return "Kyauyu";
			break;
		case PT_OYCH:
			return "Oych";
			break;
		case PT_RYOULAG:
			return "Ryoulag";
			break;
		case PT_TUN_DOUAARI:
			return "TunDouaari";
			break;
		default:
			throw string("Planet::stringFromPlanetType: illegal planet type!");
			break;
	}
}
